package ar.edu.itba.pod.legajo48062;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

import ar.edu.itba.balance.api.AgentsTransfer;
import ar.edu.itba.balance.api.NodeAgent;
import ar.edu.itba.event.EventInformation;
import ar.edu.itba.event.RemoteEventDispatcher;
import ar.edu.itba.node.Node;
import ar.edu.itba.node.NodeInformation;
import ar.edu.itba.pod.agent.runner.Agent;

public class DistributedAgentsTransfer extends UnicastRemoteObject implements AgentsTransfer {
	private static final long serialVersionUID = 1L;

	private DistributedSimulation node;
	DistributedEventDispatcher remoteEventDispatcher;
	
	public DistributedAgentsTransfer(DistributedSimulation node, DistributedEventDispatcher remoteEventDispatcher) throws RemoteException {
		super();
		this.node=node;
		this.remoteEventDispatcher=remoteEventDispatcher;
	}
	
	@Override
	public int getNumberOfAgents() throws RemoteException {
		return node.agentsRunning();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void runAgentsOnNode(List<NodeAgent> agents) throws RemoteException {
		System.out.println("PREVIO A RUNAGENTSONNODE DE "+agents.size()+" AGENTES");
		//synchronized (DistributedAgentsBalancer.class) {
			System.out.println("ME HICIERON RUNAGENTSONNODE DE "+agents.size()+" AGENTES");
			
			for (final NodeAgent nodeAgent : agents){
				System.out.println("ESTOY CORRIENDO UN AGENTE QUE ERA DE "+nodeAgent.node().id());
				if (!node.getCluster().me().equals(nodeAgent.node())){
					System.out.println("LE PIDO NEWEVENTSFOR A "+nodeAgent.node().id());
					Set<EventInformation> newEvents = (Set<EventInformation>) new Communication<RemoteEventDispatcher>(node.getCluster(),Node.DISTRIBUTED_EVENT_DISPATCHER){
						@Override
						public Set<EventInformation> remoteCallWithReturn(RemoteEventDispatcher stub, NodeInformation nodeDestination) throws Exception {
							return stub.newEventsFor(node.getCluster().me());
						}
					}.unicastWithReturn(nodeAgent.node(), true);
					
					if (newEvents!=null){
						for (EventInformation event : newEvents){
							try {
								if (remoteEventDispatcher.publish(event)){
									System.out.println("RECUPERE UN EVENTO QUE NO TENIA ANTES DE PEDIR UN MOVEQUEUEFOR!!");
								}
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
					
					System.out.println("LE LLAMO EL MOVEQUEUEFOR A "+nodeAgent.node().id());
					boolean sucess =  new Communication<RemoteEventDispatcher>(node.getCluster(),Node.DISTRIBUTED_EVENT_DISPATCHER){
						@Override
						public void remoteCall(RemoteEventDispatcher stub, NodeInformation node) throws RemoteException {
							BlockingQueue<Object> queue = stub.moveQueueFor(nodeAgent.agent());
							remoteEventDispatcher.getLocalDispatcher().setAgentQueue(nodeAgent.agent(), queue);
						}
					}.unicast(nodeAgent.node(), true);
					
					if (sucess){
						System.out.println("TERMINE DE PEDIRLE EL MOVEQUEUEFOR A "+nodeAgent.node()+" Y LA AGREGUE LOCAL");
					}else{
						System.out.println("HUBO UN ERROR AL PEDIR EL MOVEQUEUEFOR A "+nodeAgent.node());
					}
				}
				node.addLocal(nodeAgent.agent());
			}
		//}
	}

	@Override
	public List<NodeAgent> stopAndGet(int numberOfAgents) throws RemoteException {
		List<NodeAgent> agentsToReturn=new ArrayList<NodeAgent>();
		int i=0;
		for (Agent a : node.getAgentsRunning()){
			node.remove(a);
			agentsToReturn.add(new NodeAgent(node.getCluster().me(), a));
			i++;
			if (i==numberOfAgents) break;
		}
		System.out.println("ME HICIERON STOP AND GET DE "+agentsToReturn.size()+" AGENTES");
		return agentsToReturn;
	}

}
